package minov.parser;

import minov.parser.utils.StringUtils;

/**
 * Tool for printing help definition of defined arguments in
 * {@link ArgumentParser parser} to standard
 * output.
 * ArgumentHelpPrinter will get all help
 * {@link ArgumentObject.setHelpDescription descriptions} of defined arguments
 * and prints them wrapped to {@link setLineWidth line widh}.
 * 
 * @see printHelp
 * @author Jakub Michalko
 */
public class ArgumentHelpPrinter {

   /**
    * Default constructor. There is no need to set any attributes.
    * All features can be set by specific functions.
    */
   public ArgumentHelpPrinter() {
   }

   /**
    * Characters per one line
    */
   private int _lineWidth = 60;

   /**
    * Set line width. How many characters can per printed in one line maximally.
    * 
    * @param[in] lineWidth character count
    */
   public void setLineWidth(int lineWidth) {
      this._lineWidth = lineWidth;
   }

   /**
    * Constant of no offset on line
    */
   private static int NO_OFFSET = 0;

   /**
    * Default empty space character used for creating offset of
    * full description.
    */
   private static char SPACE_CHARACTER = ' ';

   /**
    * Offset character for creating offset of full description. Default is
    * set to empty space.
    * 
    * @see setDescriptionOffset
    */
   private Character _offsetCharacter = SPACE_CHARACTER;

   /**
    * Change offset character. Offset character is used for offsetting full
    * description. Default is set to empty space.
    * 
    * @param[in] newOffsetCharacter new offset character
    * @see setDescriptionOffset
    */
   public void setOffsetCharacter(Character newOffsetCharacter) {
      _offsetCharacter = newOffsetCharacter;
   }

   /**
    * Wrap and print text from the beginning of line (with no offset).
    * 
    * @param[in] text input string
    * @see printTextWithOffset
    */
   private void printWrappedText(String text) {
      printTextWithOffset(text, NO_OFFSET);
   }

   /**
    * Wrap text according to {@link setLineWidth lineWidth} and offset and print
    * it.
    * 
    * @param[in] text input text, that will be wrapped
    * @param[in] offset characters count
    */
   private void printTextWithOffset(String text, int offset) {

      // create offset string
      String offsetText = StringUtils.repeatCharacter(_offsetCharacter, offset);
      // calculate maximal text width according to line width and offset
      int textWidth = _lineWidth - offsetText.length();
      // split text
      String[] headerLines = StringUtils.wrapText(text, textWidth);
      // printe offset and slited text on one line
      for (String line : headerLines) {
         System.out.print(offsetText);
         System.out.println(line);
      }
   }

   /**
    * Default offset
    * 
    * @see setDescriptionOffset
    */
   private static int DEFAULT_OFFSET = 4;

   /**
    * Offset of description in characters.
    * 
    * @see setDescriptionOffset
    */
   private int _descriptionOffset = DEFAULT_OFFSET;

   /**
    * Offset of full description.
    * Description offset is used for offsetting full description from the line
    * begining. Defaul offset is set to 4
    * For example if offset is set to 3, printed help will be:
    * 
    * <pre>
    * -f=FORMAT, --format=FORMAT
    *   Specify output format, possibly overriding the format specified
    *         in the environment variable TIME
    * -v, --version
    *    Print actual version of application.
    * </pre>
    * 
    * And if it will be change to 10, printed help will be:
    * 
    * <pre>
    * -f=FORMAT, --format=FORMAT
    *           Specify output format, possibly overriding the format 
    *           specified in the environment variable TIME
    * -v, --version
    *           Print actual version of application.
    * </pre>
    * 
    * @param[in] offset count of characters
    */
   public void setDescriptionOffset(int offset) {
      this._descriptionOffset = offset;
   }

   /**
    * Short header description
    */
   private String _shortHeader = "";

   /**
    * Set short header. This text will be show at {@link printShortHelp} as only
    * text.
    * 
    * Example:
    * 
    * <pre>
    * time [options] command [arguments...]
    * </pre>
    * 
    * @param [in] headerText short header text
    * @see printHelp
    * @see printShortHelp
    */
   public void setShortHeader(String headerText) {
      this._shortHeader = headerText;
   }

   /**
    * Full header description
    */
   private String _descriptionHead = "";

   /**
    * Set text of full head description that will be printed at printing whole
    * help.
    * 
    * Example:
    * Text <code>
    * The  time  command  runs  the  specified program command with the given
    * arguments.  When command finishes, time writes a  message  to  standard
    * error  giving  timing statistics about this program run.
    * </code> will be printed (with {@link setLineWidth line width} 50):
    * 
    * <pre>
    * time [options] command [arguments...]
    * 
    * The time command runs the specified program  
    * command with the given arguments.  When command 
    * finishes, time writes a message to standard  
    * error giving timing statistics about this program
    * run.
    * ...
    * </pre>
    * 
    * 
    * @param[in] descriptionHead header text
    * @see printHelp
    */
   public void setDescriptionHead(String descriptionHead) {
      this._descriptionHead = descriptionHead;
   }

   /**
    * Tail description text
    * 
    * @see printHelp
    */
   private String _descriptionTail = "";

   /**
    * Tail description will be printed as the last text in {@link printHelp}
    * 
    * @param[in] descriptionTail text
    * @see printHelp
    */
   public void setDescrtiptionTail(String descriptionTail) {
      this._descriptionTail = descriptionTail;
   }

   /**
    * Wrap accodring to {@link setLineWidth line width} and print short header .
    * 
    * <pre>
    * time [options] command [arguments...]
    * </pre>
    * 
    * @see setShortHeader
    */
   public void printShortHelp() {
      printWrappedText(_shortHeader);
   }

   /**
    * Print empty line for separating help descriptions.
    */
   private void printEmptLine() {
      System.out.println();
   }

   /**
    * Print whole help description.
    * Print order:<br>
    * - {@link setShortHeader short header}, <br>
    * - {@link setDescriptionHead full header}, <br>
    * - Formated {@link ArgumentObject.setHelpDescription Help descriptions} of
    * arguments, <br>
    * - {@link setDescrtiptionTail tail}, <br>
    * 
    * Example output:
    * 
    * <pre>
    * time [options] command [arguments...]
    * 
    * The time command runs the specified program  
    * command with the given arguments.  When command 
    * finishes, time writes a message to standard  
    * error giving timing statistics about this program
    * run.
    * 
    * -f FORMAT, --format=FORMAT
    *       Specify output format, possibly overriding the format specified
    *       in the environment variable TIME.
    * -p, --portability
    *       Use the portable output format.
    * -o FILE, --output=FILE
    *       Do not send the results to stderr, but overwrite the specified file.
    * -a, --append
    *       (Used together with -o.) Do not overwrite but append.
    * -v, --verbose
    *       Give very verbose output about all the program knows about.
    * </pre>
    * 
    * @param[in] parser source of arguments, which help descriptions will be
    *            printed
    * @see @link ArgumentObject.setHelpDescription
    */
   public void printHelp(ArgumentParser parser) {

      printShortHelp();
      printEmptLine();
      printWrappedText(_descriptionHead);
      printEmptLine();
      printArgumentsDescription(parser);
      printWrappedText(_descriptionTail);

   }

   /**
    * Get description for each argument and print id wrapped.
    * 
    * @param[in] parser source of arguments, which help descriptions will be
    *            printed
    */
   private void printArgumentsDescription(ArgumentParser parser) {

      ArgumentObject[] arguments = parser.getAllDefinedArguments();
      for (ArgumentObject printedArgument : arguments) {

         String shortDescription = printedArgument.getShortHelpDescription();
         if (StringUtils.isEmptyString(shortDescription) == false) {

            printWrappedText(shortDescription);
            String fullDescription = printedArgument.getFullHelpDescription();
            if (StringUtils.isEmptyString(fullDescription) == false) {
               printTextWithOffset(fullDescription, _descriptionOffset);
            }
            printEmptLine();
         }
      }

   }

}
